package mysql

import (
	"context"
	"database/sql"
	"github.com/go-sql-driver/mysql"
	"go-quick/interface/iorm"
	"regexp"
	"strconv"
	"strings"
)

// Dialector mysql的方言实现
type Dialector struct {
	DriverName                    string
	ServerVersion                 string
	DSN                           string
	DSNConfig                     *mysql.Config
	Conn                          iorm.IConnPool
	SkipInitializeWithVersion     bool
	DefaultStringSize             uint
	DefaultDatetimePrecision      *int
	DisableWithReturning          bool
	DisableDatetimePrecision      bool
	DontSupportRenameIndex        bool
	DontSupportRenameColumn       bool
	DontSupportForShareClause     bool
	DontSupportNullAsDefaultValue bool
	DontSupportRenameColumnUnique bool
}

// defaultDatetimePrecision 默认的时间精度
var defaultDatetimePrecision = 3

func Open(dsn string) *Dialector {
	dsnConf, _ := mysql.ParseDSN(dsn)
	return &Dialector{
		DSN:       dsn,
		DSNConfig: dsnConf,
	}
}

// Name 返回数据库方言
func (dialector *Dialector) Name() string {
	return "mysql"
}

// Initialize 用于初始化数据库方言, 在ORM打开数据库连接时调用
func (dialector *Dialector) Initialize(db iorm.IDatabase) (err error) {

	var checkVersion = func(newVersion, oldVersion string) bool {
		if newVersion == oldVersion {
			return true
		}

		var (
			versionTrimmerRegexp = regexp.MustCompile(`^(\d+).*$`)

			newVersions = strings.Split(newVersion, ".")
			oldVersions = strings.Split(oldVersion, ".")
		)
		for idx, nv := range newVersions {
			if len(oldVersions) <= idx {
				return true
			}

			nvi, _ := strconv.Atoi(versionTrimmerRegexp.ReplaceAllString(nv, "$1"))
			ovi, _ := strconv.Atoi(versionTrimmerRegexp.ReplaceAllString(oldVersions[idx], "$1"))
			if nvi == ovi {
				continue
			}
			return nvi > ovi
		}

		return false
	}

	if dialector.DriverName == "" {
		dialector.DriverName = "mysql"
	}
	if dialector.DefaultDatetimePrecision == nil {
		dialector.DefaultDatetimePrecision = &defaultDatetimePrecision
	}

	if dialector.Conn != nil {
		db.Pool(dialector.Conn)
	} else {
		pool, err := sql.Open(dialector.DriverName, dialector.DSN)
		if err != nil {
			return err
		}
		db.Pool(pool)
	}

	withReturning := false
	if !dialector.SkipInitializeWithVersion {
		err = db.Conn().QueryRowContext(context.Background(), "SELECT VERSION()").Scan(&dialector.ServerVersion)
		if err != nil {
			return err
		}

		if strings.Contains(dialector.ServerVersion, "MariaDB") {
			dialector.DontSupportRenameIndex = true
			dialector.DontSupportRenameColumn = true
			dialector.DontSupportForShareClause = true
			dialector.DontSupportNullAsDefaultValue = true
			withReturning = checkVersion(dialector.ServerVersion, "10.5")
		} else if strings.HasPrefix(dialector.ServerVersion, "5.6.") {
			dialector.DontSupportRenameIndex = true
			dialector.DontSupportRenameColumn = true
			dialector.DontSupportForShareClause = true
		} else if strings.HasPrefix(dialector.ServerVersion, "5.7.") {
			dialector.DontSupportRenameColumn = true
			dialector.DontSupportForShareClause = true
		} else if strings.HasPrefix(dialector.ServerVersion, "5.") {
			dialector.DisableDatetimePrecision = true
			dialector.DontSupportRenameIndex = true
			dialector.DontSupportRenameColumn = true
			dialector.DontSupportForShareClause = true
		}

		if strings.Contains(dialector.ServerVersion, "TiDB") {
			dialector.DontSupportRenameColumnUnique = true
		}
	}

	if withReturning {
		// TODO
	}

	return nil

}

func (dialector *Dialector) Migrator(db iorm.IDatabase) iorm.IMigrator {
	//TODO implement me
	panic("implement me")
}

func (dialector *Dialector) DataTypeOf(field iorm.IField) string {
	//TODO implement me
	panic("implement me")
}

func (dialector *Dialector) DefaultValueOf(field iorm.IField) iorm.IExpression {
	//TODO implement me
	panic("implement me")
}

func (dialector *Dialector) BindVarTo(writer iorm.IWriter, stmt iorm.IStatement, v any) {
	//TODO implement me
	panic("implement me")
}

func (dialector *Dialector) QuoteTo(writer iorm.IWriter, s string) {
	//TODO implement me
	panic("implement me")
}

func (dialector *Dialector) Explain(sql string, vars ...any) string {
	//TODO implement me
	panic("implement me")
}
